#include "../s21_decimal.h"

void s21_extend(s21_decimal *source, s21_long_decimal *destination) {
  for (int i = 0; i < LAST; i++) {
    destination->bits[i] = source->bits[i];
  }

  destination->bits[LONG_LAST] = source->bits[LAST];
}

int s21_reduce(s21_long_decimal *source, s21_decimal *destination) {
  int status = OK;
  int discarded_digit = 0;
  int discarded_digits_sum = 0;
  int scale = s21_get_long_scale(source);

  for (; (s21_has_long_bits(source) && scale > 0) || scale > 28; scale--) {
    discarded_digit = s21_div_long_by_ten(source);
    discarded_digits_sum += discarded_digit;
  }

  s21_set_long_scale(source, scale);

  if (s21_has_long_bits(source)) {
    status = s21_get_long_sign(source) == POSITIVE ? POSITIVE_INF_ERROR
                                                   : NEGATIVE_INF_ERROR;
  } else {
    int is_not_even = s21_get_long_bit(source, 0) == 1 ? TRUE : FALSE;

    if (s21_is_not_max_value_ignore_scale(source) &&
        (discarded_digit > 5 ||
         (discarded_digit == 5 && discarded_digits_sum > discarded_digit) ||
         (discarded_digit == 5 && discarded_digits_sum == 5 && is_not_even))) {
      s21_increase_long_by_one(source);
    }

    for (int i = 0; i < LAST; i++) {
      destination->bits[i] = source->bits[i];
    }

    destination->bits[LAST] = source->bits[LONG_LAST];
  }

  return status;
}

int s21_from_int_to_decimal(int source, s21_decimal *destination) {
  if (destination == NULL) {
    return ERROR;
  }

  s21_reset(destination);

  if (source < 0) {
    s21_set_sign(destination, NEGATIVE);
  }

  if (source == INT_MIN) {
    s21_set_bit(destination, 31);
  } else {
    destination->bits[0] = source >= 0 ? source : source * -1;
  }

  return OK;
}

int s21_from_decimal_to_int(s21_decimal source, int *destination) {
  int status = s21_truncate(source, &source);

  for (int i = 1; i < LAST; i++) {
    if (source.bits[i] != 0) {
      status = ERROR;
    }
  }

  if (status == OK) {
    int sign = s21_get_sign(&source);
    unsigned int value = (unsigned int)source.bits[0];
    const unsigned int number = 2147483648u;

    if (value < number) {
      *destination = sign == POSITIVE ? (int)value : ((int)value) * -1;
    } else if (value == number && sign == NEGATIVE) {
      *destination = INT_MIN;
    } else {
      status = ERROR;
    }
  }

  return status;
}

int s21_from_decimal_to_float(s21_decimal source, float *destination) {
  if (s21_is_invalid(&source)) {
    return ERROR;
  }

  double power = 1;
  double number = 0;

  for (int i = 0; i <= MAX_INDEX; i++) {
    if (s21_get_bit(&source, i) == 1) {
      number += power;
    }

    power *= 2;
  }

  for (int i = s21_get_scale(&source); i > 0; i--) {
    number /= 10.0;
  }

  int sign = s21_get_sign(&source);
  *destination = sign == POSITIVE ? (float)number : ((float)number) * -1;

  return OK;
}

float s21_fabsf(float value) {
  return value == value && value >= 0.0 ? value : value * (-1.0);
}

int s21_from_float_to_decimal(float source, s21_decimal *destination) {
  if (source != source || s21_fabsf(source) > 3.4E+38 || destination == NULL) {
    return ERROR;
  }

  int status = OK;
  s21_reset(destination);

  if (s21_fabsf(source) < TOO_SMALL || s21_fabsf(source) > TOO_BIG) {
    status = ERROR;
  } else {
    char string[99] = "\0";
    sprintf(string, "%.6E", source);

    int value = 0;
    int e_symbol_index = s21_parse_int(string, 'E', &value);

    if (value < 0) {
      value *= -1;
      s21_set_sign(destination, NEGATIVE);
    }

    destination->bits[0] = value;

    int scale = 0;
    s21_parse_int(string + e_symbol_index + 1, '\0', &scale);
    scale -= 6;

    if (scale < 0) {
      for (; scale < 0 && (destination->bits[0] % 10 == 0); scale++) {
        destination->bits[0] /= 10;
      }

      s21_set_scale(destination, -1 * scale);
    } else if (scale > 0) {
      s21_long_decimal temp = INIT_ZERO_S21_LONG_DECIMAL;
      s21_extend(destination, &temp);

      for (; scale > 0; scale--) {
        s21_mul_long_by_ten(&temp);
      }

      s21_reduce(&temp, destination);
    }
  }

  return status;
}